{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## _*HDF5 files and HDF5 driver*_\n",
    "\n",
    "Qiskit Chemistry supports a number of different chemistry drivers, i.e chemistry programs and software libraries, which are used to compute integrals that are then used to build the second quantized Hamiltonian in the FermionicOperator.\n",
    "\n",
    "Drivers, built using the programs and libraries, include those for Gaussian 16, PyQuante, PySCF and PSI4. The main Qiskit documentation has more information on [drivers](https://qiskit.org/documentation/aqua/chemistry/qiskit_chemistry_drivers.html).\n",
    "\n",
    "When a driver is run, Qiskit Chemistry outputs the result in a common format for later processing. This output, the `QMolecule`, includes electron integrals, numbers of electrons, molecular orbital values and so on. While drivers can output different values, even for the same problem, due to computational differences, the result is still in this common format.\n",
    "\n",
    "This QMolecule then is the output of the drivers and input to the rest of the chemistry stack. Now we have a capability in the QMolecule to be save it as a file in [HDF5](https://www.hdfgroup.org/solutions/hdf5/) format. We can also load such a saved HDF5 file back into a QMolecule to re-create the original. The latter capability we built into a driver we call the HDF5 driver. This driver the file and presents a QMolecule to the chemistry stack just as the other drivers.\n",
    "\n",
    "Let's take a look at the HDF5 driver and how to create HDF5 files."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### HDF5 driver\n",
    "\n",
    "This tutorials folder has some HDF5 that were saved before. So let's first use the HDF5 driver to load one up and output a QMolecule.\n",
    "\n",
    "The HDF5 file name tell us it was an H2 molecule, at interatomic distance 0.735 with a basis set of sto-3g that came from the original driver. _Note: this naming convention is just what is used by us in the HDF5 samples here, no naming convention is enforced._\n",
    "\n",
    "We'll print some fields from the QMolecule, that are set from the file, to show a small part of its content, which indeed matches what we would expect."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Number of orbitals: 2\n",
      "Number of alpha electrons: 2\n",
      "Number of beta electrons: 2\n"
     ]
    }
   ],
   "source": [
    "from qiskit.chemistry.drivers import HDF5Driver\n",
    "\n",
    "driver = HDF5Driver('./h2_0.735_sto-3g.hdf5')\n",
    "molecule = driver.run()\n",
    "\n",
    "print('Number of orbitals: {}'.format(molecule.num_orbitals))\n",
    "print('Number of alpha electrons: {}'.format(molecule.num_orbitals))\n",
    "print('Number of beta electrons: {}'.format(molecule.num_orbitals))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To show this molecule can now be used as input to the rest of the chemistry we will compute its ground state energy. This is the electronic part that is computed. As the focus of this tutorial is around the HDF5 file and driver I am not going to explain the code below. There are other tutorials that cover this aspect. Also this uses a classical algorithm from Aqua just to keep things simpler. Again other tutorials here cover this ground state energy problem showing VQE or IQPE to solve it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-1.8572750270315885\n"
     ]
    }
   ],
   "source": [
    "from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType \n",
    "from qiskit.aqua.algorithms import NumPyMinimumEigensolver\n",
    "\n",
    "core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, \n",
    "                   two_qubit_reduction=True)\n",
    "qubit_op, aux_ops = core.run(molecule)\n",
    "\n",
    "ee = NumPyMinimumEigensolver(qubit_op, aux_operators=aux_ops)\n",
    "result = ee.run()\n",
    "\n",
    "print(result.eigenvalue.real)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Another field in the QMolecule is the nuclear repulsion energy. We can combine this with above result to compute the total ground state energy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-1.137306035752677\n"
     ]
    }
   ],
   "source": [
    "print(result.eigenvalue.real + molecule.nuclear_repulsion_energy)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The chemistry stack can produce a formatted result from the algorithms result. I show it here for comparison to above output."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=== GROUND STATE ENERGY ===\n",
      " \n",
      "* Electronic ground state energy (Hartree): -1.857275027032\n",
      "  - computed part:      -1.857275027032\n",
      "  - frozen energy part: 0.0\n",
      "  - particle hole part: 0.0\n",
      "~ Nuclear repulsion energy (Hartree): 0.719968991279\n",
      "> Total ground state energy (Hartree): -1.137306035753\n",
      "  Measured:: # Particles: 2.000 S: 0.000 S^2: 0.000 M: 0.00000\n",
      " \n",
      "=== DIPOLE MOMENT ===\n",
      " \n",
      "* Electronic dipole moment (a.u.): [0.0  0.0  0.0]\n",
      "  - computed part:      [0.0  0.0  0.0]\n",
      "  - frozen energy part: [0.0  0.0  0.0]\n",
      "  - particle hole part: [0.0  0.0  0.0]\n",
      "~ Nuclear dipole moment (a.u.): [0.0  0.0  0.0]\n",
      "> Dipole moment (a.u.): [0.0  0.0  0.0]  Total: 0.0\n",
      "               (debye): [0.0  0.0  0.0]  Total: 0.0\n"
     ]
    }
   ],
   "source": [
    "full_result = core.process_algorithm_result(result)\n",
    "print(full_result)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### HDF5 file\n",
    "\n",
    "Lets look at creation of the HDF5 file. There can be many reasons you might want to do this, for example to ensure you are always testing from the exact same content.\n",
    "\n",
    "Lets first create a QMolecule instance using the PySCF driver. The same can be done with other drivers too."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Number of orbitals: 2\n",
      "Number of alpha electrons: 2\n",
      "Number of beta electrons: 2\n"
     ]
    }
   ],
   "source": [
    "from qiskit.chemistry.drivers import PySCFDriver, UnitsType\n",
    "\n",
    "driver = PySCFDriver(atom='H .0 .0 .0; H .0 .0 0.735', unit=UnitsType.ANGSTROM,\n",
    "                     charge=0, spin=0, basis='sto3g')\n",
    "molecule = driver.run()\n",
    "\n",
    "print('Number of orbitals: {}'.format(molecule.num_orbitals))\n",
    "print('Number of alpha electrons: {}'.format(molecule.num_orbitals))\n",
    "print('Number of beta electrons: {}'.format(molecule.num_orbitals))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here we save the molecule, in the HDF5 format, by passing in a file name to the save() method. We will use here a temporary file (and delete it once we are done) but it's good practice to name the files after what it represents, such as the example hdf5 file used above, if you intend to save these for future use."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "/var/folders/ln/qtvvmkp97n9dvwtwgmcrrmkh0000gn/T/tmp70037kfh.hdf5 : 21768 bytes\n"
     ]
    }
   ],
   "source": [
    "import os, tempfile\n",
    "\n",
    "fd, hdf5_file = tempfile.mkstemp(suffix='.hdf5')\n",
    "os.close(fd)\n",
    "\n",
    "molecule.save(hdf5_file)\n",
    "print('{} : {} bytes'.format(hdf5_file, os.path.getsize(hdf5_file)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With the HDF5 file we can now re-create the original QMolecule by passing the file to the HDF5 driver just as we did in the above section."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Number of orbitals: 2\n",
      "Number of alpha electrons: 2\n",
      "Number of beta electrons: 2\n"
     ]
    }
   ],
   "source": [
    "driver = HDF5Driver(hdf5_file)\n",
    "molecule1 = driver.run()\n",
    "\n",
    "print('Number of orbitals: {}'.format(molecule1.num_orbitals))\n",
    "print('Number of alpha electrons: {}'.format(molecule1.num_orbitals))\n",
    "print('Number of beta electrons: {}'.format(molecule1.num_orbitals))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "os.remove(hdf5_file)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Saving sets of HDF5 files\n",
    "\n",
    "The above code can be used in a loop updating values to create a set of HDF5 files. For example you might want a set that span of a set of inter-atomic distances for a dissociation curve, or the same molecule with different basis sets. You can see many examples in the tutorials of looping over values to plot dissociation curves etc., so I am not going to show more code here. But this is simple to do knowing about the basics above. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
